package com.xmqsfsky1017.skysrf.utils.tree;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.ByteArrayInputStream;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 树点类，表示树结构中的一个节点
 */
public class WeakerTree {
    /* 节点值 */
    private String value;
    
    /* 父节点 */
    private WeakerTree mom;
    
    /* 子节点映射表 */
    private final Map<String, WeakerTree> sons = new HashMap<>();
    
    /* 果实编码集合 */
    private final Set<String> fruits = new HashSet<>();

    /**
     * 树形结构常量定义
     */
    public enum TreeConst {
        /* 表示节点不存在的常量 */
        NOTEXIST
    }

    /**
     * 树结构操作异常
     */
    public static class TreeDieException extends Exception {
        /**
         * 构造函数
         * @param message 异常信息
         */
        public TreeDieException(String message) {
            super(message);
        }
    }

    /**
     * 创建根节点
     * @param value 节点值
     */
    public WeakerTree(String value) {
        this(value, null);
    }

    /**
     * 创建节点
     * @param value 节点值
     * @param mom 父节点
     */
    public WeakerTree(String value, WeakerTree mom) {
        this.value = value;
        this.mom = mom;
    }

    /**
     * 获取或创建路径上的节点
     * @param path 节点路径（如"abc"）
     * @return 路径末端的节点
     */
    public WeakerTree getOrCreate(String path) {
        if ("$".equals(path)) {
            return this;
        }
        WeakerTree current = this;
        for (char c : path.toCharArray()) {
            String key = String.valueOf(c);
            current.sons.putIfAbsent(key, new WeakerTree(key, current));
            current = current.sons.get(key);
        }
        return current;
    }

    /**
     * 在指定路径挂载果实
     * @param path 目标路径
     * @param fruit 果实编码
     * @throws TreeDieException 果实编码为空时抛出异常
     */
    public void grow(String path, String fruit) throws TreeDieException {
        if (fruit == null) {
            throw new TreeDieException("果实编码不能为null");
        }
        getOrCreate(path).fruits.add(fruit);
    }

    /**
     * 删除指定路径的整个节点
     * @param road 目标路径
     */
    public void remove(String road) {
        remove(road, null);
    }

    /**
     * 删除节点或果实
     * @param road 目标路径
     * @param value 要删除的值（null表示删除整个节点）
     */
    public void remove(String road, String value) {
        if (!isExist(road)) return;
        
        WeakerTree target = getOrCreate(road);
        if (value == null) {
            // 删除整个节点
            target.sons.clear();
            target.fruits.clear();
        } else {
            // 删除指定果实
            target.fruits.remove(value);
            // 删除值为指定value的子节点
            target.sons.entrySet().removeIf(entry -> 
                value.equals(entry.getValue().value)
            );
        }
    }

    /**
     * 查找所有包含果实的节点
     * @param road 起始路径
     * @return 包含果实的节点列表
     */
    public List<WeakerTree> findAllFruit(String road) {
        List<WeakerTree> result = new ArrayList<>();
        if (!isExist(road)) return result;
        
        Deque<WeakerTree> stack = new ArrayDeque<>();
        stack.push(getOrCreate(road));
        
        while (!stack.isEmpty()) {
            WeakerTree node = stack.pop();
            if (!node.fruits.isEmpty()) {
                result.add(node);
            }
            // 添加所有子节点到栈中
            for (WeakerTree son : node.sons.values()) {
                stack.push(son);
            }
        }
        return result;
    }

    /**
     * 检查路径是否存在
     * @param road 要检查的路径
     * @return 路径是否存在
     */
    public boolean isExist(String road) {
        if ("$".equals(road)) return true;
        
        WeakerTree current = this;
        for (char c : road.toCharArray()) {
            String key = String.valueOf(c);
            if (!current.sons.containsKey(key)) {
                return false;
            }
            current = current.sons.get(key);
        }
        return true;
    }

    /**
     * 查找果实对应的所有路径
     * @param fruitValue 果实编码
     * @return 包含该果实的路径列表
     */
    public List<String> checkCode(String fruitValue) {
        return findAllFruit("$").stream()
                .filter(p -> p.fruits.contains(fruitValue))
                .map(this::getPath)
                .collect(Collectors.toList());
    }

    /**
     * 构建果实编码到路径的索引
     * @return 索引映射表 {果实编码: [路径1, 路径2]}
     */
    public Map<String, List<String>> buildIndex() {
        Map<String, List<String>> index = new HashMap<>();
        for (WeakerTree node : findAllFruit("$")) {
            String path = getPath(node);
            for (String fruit : node.fruits) {
                index.computeIfAbsent(fruit, k -> new ArrayList<>()).add(path);
            }
        }
        return index;
    }

    /**
     * 获取节点路径（从根到当前节点）
     * @param node 目标节点
     * @return 节点路径字符串
     */
    private String getPath(WeakerTree node) {
        LinkedList<String> path = new LinkedList<>();
        WeakerTree current = node;
        // 从当前节点向上回溯到根节点（不包括根节点）
        while (current != null && current.mom != null) {
            path.addFirst(current.value);
            current = current.mom;
        }
        return String.join("", path);
    }

    /**
     * 生成XML表示
     * @return XML字符串
     */
    public String toXml() {
        return toXml("", this);
    }

    /**
     * 递归生成XML
     * @param indent 当前缩进
     * @param node 当前节点
     * @return XML字符串片段
     */
    private String toXml(String indent, WeakerTree node) {
        StringBuilder sb = new StringBuilder();
        String attrs = "value='" + node.value + "'";
        
        if (!node.fruits.isEmpty()) {
            String fruits = String.join(",", node.fruits);
            attrs += " fruit='" + fruits + "'";
        }
        
        if (node.sons.isEmpty()) {
            sb.append(indent).append("<node ").append(attrs).append("/>\n");
        } else {
            sb.append(indent).append("<node ").append(attrs).append(">\n");
            // 按节点值排序以确保一致输出
            node.sons.values().stream()
                .sorted(Comparator.comparing(p -> p.value))
                .forEach(son -> sb.append(toXml(indent + "    ", son)));
            sb.append(indent).append("</node>\n");
        }
        return sb.toString();
    }

    /**
     * 获取树结构的字符串表示（XML格式）
     * @return XML字符串
     */
    @Override
    public String toString() {
        return toXml().trim();
    }

    /**
     * 从XML字符串加载树结构
     * @param xmlString XML字符串
     * @return 根节点
     * @throws TreeDieException XML解析失败时抛出
     */
    public static WeakerTree load(String xmlString) throws TreeDieException {
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document doc = builder.parse(new ByteArrayInputStream(xmlString.getBytes()));
            return parseNode(doc.getDocumentElement(), null);
        } catch (Exception e) {
            throw new TreeDieException("XML解析失败");
        }
    }

    /**
     * 递归解析XML节点
     * @param elem XML元素
     * @param parent 父节点
     * @return 解析后的节点
     */
    private static WeakerTree parseNode(Element elem, WeakerTree parent) {
        String value = elem.getAttribute("value");
        WeakerTree node = new WeakerTree(value, parent);
        
        if (elem.hasAttribute("fruit")) {
            String fruits = elem.getAttribute("fruit");
            if (!fruits.isEmpty()) {
                String[] fruitArray = fruits.split(",");
                for (String fruit : fruitArray) {
                    if (!fruit.isEmpty()) {
                        node.fruits.add(fruit);
                    }
                }
            }
        }
        
        NodeList children = elem.getChildNodes();
        for (int i = 0; i < children.getLength(); i++) {
            Node child = children.item(i);
            if (child.getNodeType() == Node.ELEMENT_NODE) {
                Element childElem = (Element) child;
                WeakerTree childNode = parseNode(childElem, node);
                node.sons.put(childNode.value, childNode);
            }
        }
        return node;
    }
}
